package com.googlecode.cswish.view;

import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.struts2.components.Component;

import com.googlecode.cswish.util.FreeMarkerHelper;
import com.opensymphony.xwork2.util.ValueStack;

import freemarker.core.Environment;
import freemarker.core.TemplateElement;

/**
 * Run the template which defined in the 'extends' tag
 * 
 * @author Jerry.Feng Date: 2008-6-2
 */
public class Mark extends Component {
	
	public final static String EXTEND_POINTS = "_extend_point";
	public final static String EXTEND_POINT_PAGES = "_extend_point_page";
	public final static String EXT_SUFFIX = ".ext";
	public final static String EXTEND_NAME_MAPPING = "_extend_point_namemapping";
	
    public Mark(ValueStack stack) {
		super(stack);
	}

    protected String name;

    public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	public boolean start(Writer writer) {
		// check the old value
		Object extension = getExtension(stack, name);
		
		if (extension != null) {
			// save current block
			Environment environment = Environment.getCurrentEnvironment();
			List<TemplateElement> elementStack = FreeMarkerHelper.getEnvElementStack(environment);
			TemplateElement element = (TemplateElement) elementStack.get(elementStack.size() - 1);
			TemplateElement nestedBlock = FreeMarkerHelper.getEnvNestedBlock(element);
			getExtensions(stack).put(name, nestedBlock);
			
			// execute the extension
			FreeMarkerHelper.execute(environment, extension, writer);
			return false;
		} else {
			return true;
		}
    }
	
	////////////////////// Help method for communicating among tags ///////////
	private static Map<String, Object> getExtensionPages(ValueStack stack) {
		Map<String, Object> extendPointPages = (Map<String, Object>) stack.getContext().get(EXTEND_POINT_PAGES);
		if (extendPointPages == null) {
			extendPointPages = new HashMap<String, Object>();
			stack.getContext().put(EXTEND_POINT_PAGES, extendPointPages);
		}
		return extendPointPages;
	}
	
	public static Map<String, Object> getExtensions(ValueStack stack) {
		Map<String, Object> extendPoints = (Map<String, Object>) stack.getContext().get(EXTEND_POINTS);
		if (extendPoints == null) {
			extendPoints = new HashMap<String, Object>();
			stack.getContext().put(EXTEND_POINTS, extendPoints);
		}
		return extendPoints;
	}
	
	public static List<String> getExtensionNames(ValueStack stack, String page) {
		Collection<String> keys = getExtensions(stack).keySet();
		
		List<String> names = new ArrayList<String>(keys.size());
		for (String key : keys) {
			if (key.endsWith(EXT_SUFFIX)) {
				if (page == null || page != null && page.equals(getExtensionPages(stack).get(key))) {
					names.add(key.substring(0, key.length() - EXT_SUFFIX.length()));
				}
			}
		}
		return names;
	}
	
	public static void addExtensionFor(ValueStack stack, String name, String page, TemplateElement block) {
		String key = name + EXT_SUFFIX;
		getExtensions(stack).put(key, block);
		if (page != null) {
			getExtensionPages(stack).put(key, page);
			
			String formalName = getExtensionName(name);
			getExtensions(stack).put(formalName + EXTEND_NAME_MAPPING, name);
		}	
	}
	
	public static void addExtendsionPage(ValueStack stack, String name, String page) {
		String key = name + EXT_SUFFIX;
		getExtensionPages(stack).put(key, page);
		
		String formalName = getExtensionName(name);
		getExtensions(stack).put(formalName + EXTEND_NAME_MAPPING, name);
	}
	
	public static void addExtensionFor(ValueStack stack, String name, String page, String block) {
		String key = name + EXT_SUFFIX;
		getExtensions(stack).put(key, block);
		if (page != null) {
			getExtensionPages(stack).put(key, page);
			
			String formalName = getExtensionName(name);
			getExtensions(stack).put(formalName + EXTEND_NAME_MAPPING, name);
		}
	}
	
	public static String getExtensionName(String name) {
		StringBuilder sb = new StringBuilder(name.length());
		for (char c : name.toCharArray()) {
			if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '_') {
				sb.append(c);
			}
		}
		return sb.toString();
	}
	
	public static Object getExtension(ValueStack stack, String formalName) {
		String name = (String) getExtensions(stack).get(formalName + EXTEND_NAME_MAPPING);
		return getExtensions(stack).get(name + EXT_SUFFIX);
	}
}